#include "can_thread.h"

#include <QDebug>
#include <QTime>
#include <QCoreApplication>


CanThread::CanThread()
    : stopped(false), m_deviceType(0), m_debicIndex(0)
    , m_baudRate(0), m_debicCom(0)
{
}

void CanThread::Stop()
{
    stopped = true;
}

bool CanThread::OpenDevice(UINT deviceType, UINT debicIndex, UINT baudRate)
{
    m_deviceType = deviceType;
    m_debicIndex = debicIndex;
    m_baudRate = baudRate;

    DWORD dwRel = VCI_OpenDevice(m_deviceType, m_debicIndex, 0);
    if (dwRel != 1) {
        QString errStr = QString("USB-CAN[%1, %2] is offline").arg(m_deviceType).arg(m_debicIndex);
        qDebug() << errStr;
        return false;
    }
    else {
        QString str = QString("Open USB-CAN[%1, %2] success").arg(m_deviceType).arg(m_debicIndex);
        qDebug() << str;
    }

    return true;
}

bool CanThread::InitCan()
{
    DWORD dwRel;
    for (int i = 0; i < kChannelNum; i++) {
        dwRel = VCI_ClearBuffer(m_deviceType, m_debicIndex, i);
        if (dwRel != 1) {
            QString errStr = QString("Clear buffer %1 fail").arg(i);
            qDebug() << errStr;
        }
    }

    VCI_INIT_CONFIG vic;
    vic.AccCode = 0x80000008;
    vic.AccMask = 0xFFFFFFFF;
    vic.Filter = 1;
    vic.Mode = 0;

    /*
     * Configure buad rate
     */
    switch (m_baudRate) {
    case 10:
        vic.Timing0=0x31;
        vic.Timing1=0x1c;
        break;
    case 20:
        vic.Timing0=0x18;
        vic.Timing1=0x1c;
        break;
    case 40:
        vic.Timing0=0x87;
        vic.Timing1=0xff;
        break;
    case 50:
        vic.Timing0=0x09;
        vic.Timing1=0x1c;
        break;
    case 80:
        vic.Timing0=0x83;
        vic.Timing1=0xff;
        break;
    case 100:
        vic.Timing0=0x04;
        vic.Timing1=0x1c;
        break;
    case 125:
        vic.Timing0=0x03;
        vic.Timing1=0x1c;
        break;
    case 200:
        vic.Timing0=0x81;
        vic.Timing1=0xfa;
        break;
    case 250:
        vic.Timing0=0x01;
        vic.Timing1=0x1c;
        break;
    case 400:
        vic.Timing0=0x80;
        vic.Timing1=0xfa;
        break;
    case 500:
        vic.Timing0=0x00;
        vic.Timing1=0x1c;
        break;
    case 666:
        vic.Timing0=0x80;
        vic.Timing1=0xb6;
        break;
    case 800:
        vic.Timing0=0x00;
        vic.Timing1=0x16;
        break;
    case 1000:
        vic.Timing0=0x00;
        vic.Timing1=0x14;
        break;
    case 33:
        vic.Timing0=0x09;
        vic.Timing1=0x6f;
        break;
    case 66:
        vic.Timing0=0x04;
        vic.Timing1=0x6f;
        break;
    case 83:
        vic.Timing0=0x03;
        vic.Timing1=0x6f;
        break;
    default:
        break;
    }

    for (int i = 0; i < kChannelNum; i++) {
        dwRel = VCI_InitCAN(m_deviceType, m_debicIndex, 0, &vic);
        if (dwRel != 1) {
            QString errStr = QString("Init failure for channel %1").arg(i);
            qDebug() << errStr;
            return false;
        }
        else {
            QString str = QString("Init success for channel %1").arg(i);
            qDebug() << str;
        }
    }

    VCI_BOARD_INFO vbi;
    dwRel = VCI_ReadBoardInfo(m_deviceType, m_debicIndex, &vbi);
    if (dwRel != 1) {
        qDebug() << "Read board info failure";
        return false;
    }
    else {
        emit BoardInfo(vbi);
    }
    return true;
}

bool CanThread::StartCan()
{
     for (int i = 0; i < kChannelNum; i++) {
        if (VCI_StartCAN(m_deviceType, m_debicIndex, i) != 1) {
            QString errStr = QString("Start %1 fail").arg(i);
            qDebug() << errStr;
            return false;
        }
        else {
            QString str = QString("Start %1 success").arg(i);
            qDebug() << str;
        }
     }

    return true;
}

bool CanThread::SendData(UINT channel, UINT id, BYTE remoteFlag,
              BYTE externFlag, const uint8_t * data, BYTE len)
{
    DWORD dwRel;
    VCI_CAN_OBJ vco;
    vco.ID = id;
    vco.RemoteFlag = remoteFlag;
    vco.ExternFlag = externFlag;
    vco.DataLen = len;
    for (int i = 0; i < len; i++) {
        vco.Data[i] = data[i];
    }

    dwRel = VCI_Transmit(m_deviceType, m_debicIndex, channel, &vco, 1);
    if (dwRel < 0) {
        QString errStr = QString("Transmit data fail, device type: %1, debic index: %2, channel: %3. Possible reson: device is offline.")
            .arg(m_deviceType)
            .arg(m_debicIndex)
            .arg(channel);
        qDebug() << errStr;
        return false;
    }

    return true;
}

void CanThread::CloseDeice()
{
    VCI_CloseDevice(m_deviceType, m_debicCom);
}

bool CanThread::ResetCan()
{
    for (int i = 0; i < kChannelNum; i++) {
        if (VCI_ResetCAN(m_deviceType, m_debicCom, i) != 1) {
            QString errStr = QString("Reset %1 fail").arg(i);
            qDebug() << errStr;
            return false;
        }
        else {
            QString str = QString("Reset %1 success").arg(i);
            qDebug() << str;
        }
    }

    return true;
}

void CanThread::run()
{
    Qt::HANDLE threadId = QThread::currentThreadId();
    qDebug() << "Current Thread ID (HANDLE): " << threadId;

    while (!stopped) {
        DWORD dwRel;
        VCI_CAN_OBJ vcos[2500];

        for (int i = 0; i < kChannelNum; i++) {
            dwRel = VCI_Receive(m_deviceType, m_debicIndex, 0, vcos, sizeof(vcos)/sizeof(vcos[0]), i);
            if (dwRel > 0) {
                emit GetProtocolData(vcos, dwRel, i);
            }
        }

        sleep(30);
    }

    stopped = false; // ???
}


void CanThread::sleep(unsigned long msec)
{
    QTime dieTime = QTime::currentTime().addMSecs(msec);

    /*
     * 循环等待超时, 等待时放弃cpu
     */
    while (QTime::currentTime() < dieTime) {
        /*
         * 强制当前线程（通常是主线程/UI线程）立即处理一部分事件队列中的事件，处理时间 <= 100ms
         */
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
    }
}

void CanThreadTest()
{
    CanThread canthread;
    canthread.OpenDevice(4, 0, 500);
    canthread.InitCan();
    canthread.StartCan();
    uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8};
    int dlc = sizeof(data) / sizeof(data[0]);
    canthread.SendData(0, 0xcc, 0, 0, data, dlc);

    canthread.CloseDeice();
}
